/****************************************************************************
 * semidrive/chips/schumacher/isp/isp_mpu_config.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/
#include <nuttx/cache.h>

#include "mpu.h"
#include "lnk_mpu.h"
#include "chip.h"

#define REG_REGION_START 0xF0000000UL
#define REG_REGION_END 0xFFFFFFFFUL

#if defined(CONFIG_BOOT_RUNFROMFLASH)
#define MPU_CONST_ADDR_START FLASH_CONST_START
#define MPU_CONST_ADDR_END FLASH_CONST_END
#define MPU_TEXT_ADDR_START FLASH_TEXT_START
#define MPU_TEXT_ADDR_END FLASH_TEXT_END
#define MPU_IRAM_FUNC_START FLASH_FUNC_INIT_START
#define MPU_IRAM_FUNC_END FLASH_FUNC_INIT_END
#else
#define MPU_CONST_ADDR_START IRAM_CONST_START
#define MPU_CONST_ADDR_END IRAM_CONST_END
#define MPU_TEXT_ADDR_START IRAM_TEXT_START
#define MPU_TEXT_ADDR_END IRAM_TEXT_END
#define MPU_IRAM_FUNC_START IRAM_FUNC_INIT_START
#define MPU_IRAM_FUNC_END IRAM_FUNC_INIT_END
#endif

#define MPU_MAIR_IDX_DEVICE_NG_NR_NE (0U)
#define MPU_MAIR_IDX_NORMAL_MEM_NON_CACHEABLE (1U)
#define MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA (2U)
#define MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA (3U)

/* Memory Attributes Configuration */
static const arm_mem_attrs_t g_mem_attrs = {
	.attrs = {
		{
			ARM_MPU_MEMATTR(ARM_MPU_MEMATTR_DEVICE, ARM_MPU_MEMATTR_DEVICE_nGnRnE),
			ARM_MPU_MEMATTR(ARM_MPU_MEMATTR_NORMAL_OUTER_NON_CACHEABLE,
					ARM_MPU_MEMATTR_NORMAL_INNER_NON_CACHEABLE),
			ARM_MPU_MEMATTR(ARM_MPU_MEMATTR_NORMAL_OUTER_NON_CACHEABLE,
					ARM_MPU_MEMATTR_NORMAL_INNER_WT_RA),
			ARM_MPU_MEMATTR(ARM_MPU_MEMATTR_NORMAL_OUTER_NON_CACHEABLE,
					ARM_MPU_MEMATTR_NORMAL_INNER_WT_RA_WA),
		},
		{
			0,
			0,
			0,
			0,
		},
	},
};

/* common mpu region info */
static const arm_mpu_region_t g_common_mpu_regions[] = {

	REGION_ENTRY("Cram text code section", 0, (uint32_t)CRAM_TEXT_START, (uint32_t)CRAM_TEXT_END, ARM_MPU_SH_NON,
		     ARM_MPU_AP_EL2_RO_EL10_RO, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("Iram Function code section", 1, (uint32_t)IRAM_FUNC_START, (uint32_t)IRAM_FUNC_END,
		     ARM_MPU_SH_NON, ARM_MPU_AP_EL2_RO_EL10_RO, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("Nocache rw data section", 2, (uint32_t)IRAM_NOCACHE_START, (uint32_t)IRAM_NOCACHE_END,
		     ARM_MPU_SH_OUTER, ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_NON_CACHEABLE,
		     ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("RW DATA+BSS section", 3, (uint32_t)IRAM_DATA_START, (uint32_t)IRAM_BSS_END, ARM_MPU_SH_NON,
		     ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("Iram text code section", 4, (uint32_t)MPU_TEXT_ADDR_START, (uint32_t)MPU_TEXT_ADDR_END,
		     ARM_MPU_SH_NON, ARM_MPU_AP_EL2_RO_EL10_RO, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("Ro Sec Code + Const + DataInited + iarm func  code section", 5, (uint32_t)MPU_CONST_ADDR_START,
		     (uint32_t)MPU_IRAM_FUNC_END, ARM_MPU_SH_NON, ARM_MPU_AP_EL2_RO_EL10_RO, ARM_MPU_XN,
		     MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA, ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("Register space", 6, (uint32_t)REG_REGION_START, (uint32_t)REG_REGION_END, ARM_MPU_SH_OUTER,
		     ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_XN, MPU_MAIR_IDX_DEVICE_NG_NR_NE, ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("IRAM_LP", 7, (uint32_t)IRAM_LP_START, (uint32_t)IRAM_LP_END, ARM_MPU_SH_NON,
		     ARM_MPU_AP_EL2_RO_EL10_RO, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("DFLASH_R", 8, (uint32_t)FLASH_DFLASH_START, (uint32_t)FLASH_DFLASH_END, ARM_MPU_SH_NON,
			ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
			ARM_MPU_REGION_ENABLE),

	REGION_ENTRY("DFLASH_W", 9, (uint32_t)(MPU_F_TO_M_ADDR(FLASH_DFLASH_START)), (uint32_t)(MPU_F_TO_M_ADDR(FLASH_DFLASH_END)), ARM_MPU_SH_NON,
			ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_EX, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
			ARM_MPU_REGION_ENABLE),

};

/* core mpu region info */

static const arm_mpu_region_t g_core0_mpu_regions[] = {
	REGION_ENTRY("CORE0_TCMC", 10, (uint32_t)CORE0TCMC_START, (uint32_t)CORE0TCMC_END, ARM_MPU_SH_NON,
		     ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_XN, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),
};

#if (CONFIG_NR_CPUS > 1)
static const arm_mpu_region_t g_core1_mpu_regions[] = {
	REGION_ENTRY("CORE1_TCMC", 10, (uint32_t)CORE1TCMC_START, (uint32_t)CORE1TCMC_END, ARM_MPU_SH_NON,
		     ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_XN, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),
};
#endif

#if (CONFIG_NR_CPUS > 2)
static const arm_mpu_region_t g_core2_mpu_regions[] = {
	REGION_ENTRY("CORE2_TCMC", 10, (uint32_t)CORE2TCMC_START, (uint32_t)CORE2TCMC_END, ARM_MPU_SH_NON,
		     ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_XN, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),
};
#endif

#if (CONFIG_NR_CPUS > 3)
static const arm_mpu_region_t g_core3_mpu_regions[] = {
	REGION_ENTRY("CORE3_TCMC", 10, (uint32_t)CORE3TCMC_START, (uint32_t)CORE3TCMC_END, ARM_MPU_SH_NON,
		     ARM_MPU_AP_EL2_RW_EL10_RW, ARM_MPU_XN, MPU_MAIR_IDX_NORMAL_MEM_INNER_WT_RA_WA,
		     ARM_MPU_REGION_ENABLE),
};
#endif

#if (CONFIG_NR_CPUS > 4)
#error "only support max cpu num as 4"
#endif

const arm_mpu_config_t g_common_mpu_conf = {
	.regions = g_common_mpu_regions,
	.region_num = MPU_ARRAY_SIZE(g_common_mpu_regions),
};

const arm_mpu_config_t g_core_mpu_conf[CONFIG_NR_CPUS] = {
	{
		.regions = g_core0_mpu_regions,
		.region_num = MPU_ARRAY_SIZE(g_core0_mpu_regions),
	},
#if (CONFIG_NR_CPUS > 1)
	{
		.regions = g_core1_mpu_regions,
		.region_num = MPU_ARRAY_SIZE(g_core1_mpu_regions),
	},
#endif
#if (CONFIG_NR_CPUS > 2)
	{
		.regions = g_core2_mpu_regions,
		.region_num = MPU_ARRAY_SIZE(g_core2_mpu_regions),
	},
#endif
#if (CONFIG_NR_CPUS > 3)
	{
		.regions = g_core3_mpu_regions,
		.region_num = MPU_ARRAY_SIZE(g_core3_mpu_regions),
	},
#endif
};

int e3650_mpu_init(void)
{
	int cpu = up_cpu_index();

	if ((arm_el1_mpu_check_config(&g_common_mpu_conf) != 0) ||
	    (arm_el1_mpu_check_config(&(g_core_mpu_conf[cpu])) != 0)) {
		return -1;
	}

	arm_el1_mpu_disable();
	arm_el1_mpu_enable_background(0);
	arm_el1_mpu_set_mem_attrs(&g_mem_attrs);

	arm_el1_mpu_config(g_common_mpu_conf.regions, g_common_mpu_conf.region_num);
	arm_el1_mpu_config(g_core_mpu_conf[cpu].regions, g_core_mpu_conf[cpu].region_num);

	up_invalidate_icache_all();
	up_invalidate_dcache_all();
	arm_el1_mpu_enable();
	return 0;
}
